#ifndef TYPE_CHECKER_HPP
#define TYPE_CHECKER_HPP

#include "type.hpp"
#include "visitor.hpp"

#include <string>
using namespace std;

class SymbolTable;

class TypeChecker : public Visitor {
private:
    SymbolTable* _sym_table;

    bool    _has_error;
    bool    _is_func;

public:
    TypeChecker(SymbolTable* symtable, bool is_func) : _sym_table(symtable), 
    _has_error(false), _is_func(is_func) {
    }

    ~TypeChecker() {
    }

    bool has_error();
    SymbolTable* sym_table()    { return _sym_table; }

    Type* get_param_type(ParamDefNode* n);
    Type* build_func_type(FuncDefNode* n);

    Type* instantiate_generic_func(FuncDefNode* n, ListNode* type_args);

    Node* visit(Node* n);

    virtual Node* visit(PrintNode* n);
    virtual Node* visit(ReturnNode* n);
    virtual Node* visit(BinaryOp* n);
    virtual Node* visit(LogicNotNode* n);
    virtual Node* visit(ListNode* n);
    virtual Node* visit(StmtsNode* n);
    virtual Node* visit(ConstInt* n);
    virtual Node* visit(ConstDouble* n);
    virtual Node* visit(ConstString* n);
    virtual Node* visit(ConstArray* n);
    virtual Node* visit(ImportNode* n);
    virtual Node* visit(ImplementNode* n);
    virtual Node* visit(IfNode* n);
    virtual Node* visit(WhileNode* n);
    virtual Node* visit(CmpNode* n);
    virtual Node* visit(AssignNode* n);
    virtual Node* visit(VarNode* n);
    virtual Node* visit(VarDefNode* n);
    virtual Node* visit(ParamDefNode* n);
    virtual Node* visit(FuncDefNode* n);
    virtual Node* visit(RecordDefNode* n);
    virtual Node* visit(TraitDefNode* n);
    virtual Node* visit(LambdaDef* n);
    virtual Node* visit(CallNode* n);
    virtual Node* visit(SubscrNode* n);
    virtual Node* visit(DotNode* n);
    virtual Node* visit(SequenceNode* n);
    virtual Node* visit(BuiltinConst* n);
    virtual Node* visit(TupleNode* n);
    virtual Node* visit(ArrowTypeNode* n);
    virtual Node* visit(ArrayTypeNode* n);

    virtual Node* visit(TypeVarNode* n);
    virtual Node* visit(TypeFuncDefNode* n);
    virtual Node* visit(TypeVarDefNode* n);
    virtual Node* visit(TypeCallNode* n);
    virtual Node* visit(TypeConstInt* n);
    virtual Node* visit(TypeTupleNode* n);
    bool unify(Type* left, Type* right);

    void enter_type_scope();
    void exit_type_scope();

    Type* apply_args(Type* callee_type, ListNode* args);
    TypeTable* type_table();
};

enum ConstValueType {
    CVT_INT,
    CVT_DOULE,
    CVT_STRING,
    CVT_CODE_OBJECT,
    CVT_UNIT,
    CVT_TRUE,
    CVT_FALSE,
};

union ValueUnion {
    long long int_value;
    string* str_value;
    double dbl_value;
};

class EvaluatorValue {
public:
    ConstValueType cvt;
    ValueUnion value;
};

class Evaluator : public Visitor {
private:
    TypeChecker*    _type_checker;
    EvaluatorValue  _value;

public:
    Evaluator(TypeChecker* checker) : _type_checker(checker) {
    }

    long long int_value() {
        return _value.value.int_value;
    }

    Node* visit(Node* n);

    virtual Node* visit(PrintNode* n) { return nullptr; }
    virtual Node* visit(ReturnNode* n) { return nullptr; }
    virtual Node* visit(BinaryOp* n) { return nullptr; }
    virtual Node* visit(LogicNotNode* n) { return nullptr; }
    virtual Node* visit(ListNode* n) { return nullptr; }
    virtual Node* visit(ConstInt* n) { return nullptr; }
    virtual Node* visit(ConstDouble* n) { return nullptr; }
    virtual Node* visit(ConstString* n) { return nullptr; }
    virtual Node* visit(ConstArray* n) { return nullptr; }
    virtual Node* visit(ImportNode* n) { return nullptr; }
    virtual Node* visit(ImplementNode* n) { return nullptr; }
    virtual Node* visit(IfNode* n) { return nullptr; }
    virtual Node* visit(WhileNode* n) { return nullptr; }
    virtual Node* visit(CmpNode* n) { return nullptr; }
    virtual Node* visit(AssignNode* n) { return nullptr; }
    virtual Node* visit(VarNode* n) { return nullptr; }
    virtual Node* visit(VarDefNode* n) { return nullptr; }
    virtual Node* visit(ParamDefNode* n) { return nullptr; }
    virtual Node* visit(FuncDefNode* n) { return nullptr; }
    virtual Node* visit(RecordDefNode* n) { return nullptr; }
    virtual Node* visit(TraitDefNode* n) { return nullptr; }
    virtual Node* visit(LambdaDef* n) { return nullptr; }
    virtual Node* visit(CallNode* n) { return nullptr; }
    virtual Node* visit(SubscrNode* n) { return nullptr; }
    virtual Node* visit(BuiltinConst* n) { return nullptr; }
    virtual Node* visit(TupleNode* n) { return nullptr; }
    virtual Node* visit(ArrowTypeNode* n) { return nullptr; }
    virtual Node* visit(ArrayTypeNode* n) { return nullptr; }
    virtual Node* visit(TypeTupleNode* n) { return nullptr; }
    virtual Node* visit(TypeVarNode* n) { return nullptr; }
    virtual Node* visit(TypeFuncDefNode* n) { return nullptr; }
    virtual Node* visit(TypeVarDefNode* n) { return nullptr; }
    virtual Node* visit(TypeCallNode* n) { return nullptr; }
    virtual Node* visit(TypeConstInt* n);
};

#endif

